package com.xiongtian.springframework.beans.factory.support;

import com.xiongtian.springframework.beans.BeansException;
import com.xiongtian.springframework.beans.factory.DisposableBean;
import com.xiongtian.springframework.beans.factory.ObjectFactory;
import com.xiongtian.springframework.beans.factory.config.SingletonBeanRegistry;


import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: xiongtian
 * @CreateTime: 2025-03-25  10:22
 * @Version: 1.0
 * @Description: 抽象单例注册类
 * <p>
 * 主要是实现 getSingleton 方法，同时实现了一个受保护的 addSingleton 方法。
 */

public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {

    /**
     * Internal marker for a null singleton object:
     * used as marker value for concurrent Maps (which don't support null values).
     */
    protected static final Object NULL_OBJECT = new Object();


    /**
     * 一级缓存，普通对象
     */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();

    /**
     * 二级缓存，早期对象,提前暴漏对象，没有完全实例化的对象
     * 存放的是原始的对象，而不是完整的对象，需要在后续的过程中，通过反射等手段，将原始对象转换为完整的对象
     */
    protected final Map<String, Object> earlySingletonObjects = new HashMap<>();

    /**
     * 三级缓存，存放代理对象
     */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();

    /**
     * 注册销毁方法容器
     */
    private final Map<String, DisposableBean> disposableBeans = new LinkedHashMap<>();

    /**
     * 获取单例对象
     * 一级缓存 -> 二级缓存 -> 三级缓存 -> 实例化对象
     *
     * @param beanName Bean名称
     * @return Bean对象
     */
    @Override
    public Object getSingleton(String beanName) {
        Object singletonObject = singletonObjects.get(beanName);
        if (null == singletonObject) {
            singletonObject = earlySingletonObjects.get(beanName);
            // 判断二级缓存中是否有对象，这个对象就是代理对象，因为只有代理对象才会放到三级缓存中
            if (null == singletonObject) {
                ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);
                if (null != singletonFactory) {
                    singletonObject = singletonFactory.getObject();
                    // 把三级缓存中的代理对象中的真实对象获取出来，放入二级缓存中
                    earlySingletonObjects.put(beanName, singletonObject);
                    // 把三级缓存中的代理对象删除
                    singletonFactories.remove(beanName);
                }
            }
        }
        return singletonObject;
    }


    /**
     * 添加单例对象
     * 将单例对象放入到一级缓存中
     * 同时将二级，三级缓存中的对象删除
     * 因为一级缓存中的对象是完整的对象，不需要再进行后续的处理
     *
     * @param beanName        Bean名称
     * @param singletonObject Bean对象
     */
    public void registerSingleton(String beanName, Object singletonObject) {
        singletonObjects.put(beanName, singletonObject);
        earlySingletonObjects.remove(beanName);
        singletonFactories.remove(beanName);
    }


    /**
     * 添加单例工厂
     * 将单例工厂放入到三级缓存中
     * 同时将二级缓存中的对象删除
     * 因为三级缓存中的对象是代理对象，需要在后续的过程中，通过反射等手段，将代理对象转换为完整的对象
     *
     * @param beanName         bean名称
     * @param singletonFactory 单例工厂
     */
    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
        }
    }


    /**
     * 注册销毁方法
     *
     * @param beanName bean名称
     * @param bean     bean对象
     */
    public void registerDisposableBean(String beanName, DisposableBean bean) {
        disposableBeans.put(beanName, bean);
    }

    /**
     * 销毁单例对象
     */
    public void destroySingletons() {
        Set<String> keySet = this.disposableBeans.keySet();
        Object[] disposableBeanNames = keySet.toArray();
        for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
            Object beanName = disposableBeanNames[i];
            DisposableBean disposableBean = disposableBeans.remove(beanName);
            try {
                disposableBean.destroy();
            } catch (Exception e) {
                throw new BeansException("Destroy method on bean with name " + beanName + " threw an exception! ", e);
            }
        }
    }
}
